package movee.https.client;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

@SpringBootApplication
@Slf4j
public class HttpsClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(HttpsClientApplication.class, args);
    }

    @Bean
    public CommandLineRunner runner() {
        return (args) -> {
            String url = "https://localhost:8443/test/hello";
            String resp = trustAllRestTemplate().getForObject(url, String.class);
            log.info("runner response: {}", resp);
        };
    }

    @Bean
    public CommandLineRunner runner2(@Value("${trust.key-store}") String keystoreFile,
                                     @Value("${trust.key-store-password}") String pwd) {
        return (args) -> {
            String url = "https://localhost:8443/test/hello";
            String resp = trustAllSelfSignedRestTemplate(keystoreFile, pwd).getForObject(url, String.class);
            log.info("runner2 response: {}", resp);
        };
    }

    public RestTemplate trustAllRestTemplate()
            throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        // 信任策略为信任所有的https server
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                // 不需要提供truststore
                .loadTrustMaterial(null, acceptingTrustStrategy)
                .build();

        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(factory);
    }


    RestTemplate trustAllSelfSignedRestTemplate(String keystorePath, String pwd)
            throws IOException, KeyStoreException, NoSuchAlgorithmException,
            CertificateException, KeyManagementException {

        // 加载keystore
        KeyStore keyStore = loadKeyStore(keystorePath, pwd);
        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                // 认证策略为信任所有自签名证书
                .loadTrustMaterial(keyStore, new TrustSelfSignedStrategy())
                .build();

        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        factory.setConnectionRequestTimeout(5 * 60 * 1000);
        factory.setConnectTimeout(5 * 60 * 1000);
        factory.setReadTimeout(5 * 60 * 1000);

        return new RestTemplate(factory);
    }

    KeyStore loadKeyStore(String keystoreFile, String pwd)
            throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        ClassPathResource resource = new ClassPathResource(keystoreFile);
        InputStream inputStream = resource.getInputStream();
        keyStore.load(inputStream, pwd.toCharArray());
        inputStream.close();

        return keyStore;
    }

}
